建立一個「唯一」物件,專責於服務只能單一連線的情境,例如跟資料庫的溝通,同時確保全域內都可以呼叫該物件。
Singleton 是相當好懂的模式,用在只能服務單一連線的情境下,避免多重連線產生時間差,導致執行完畢的結果不是我們要的,例如:
相關作法是:
getInstance()
,負責回傳 instance
,讓全域都可以呼叫並使用。Java
因為可以跑多的執行緒(Thread
),所以在 getInstance()
除了原有的鎖之外還要多加兩道鎖:
instance
是否存在?instance
是否存在?至於 JavaScript
,不能使用常見的 class
寫法,而是使用 IIFEs(Immediately Invoked Functions Expressions),讓物件只會建立一次,沒有其他建立的方法。
Singleton: EmergencyTelephone
public class EmergencyTelephone {
private static EmergencyTelephone instance;
private EmergencyTelephone() {
}
private void useTelephone(String phoneNumber) {
System.out.println("(號碼:" + phoneNumber + ")拿起話筒");
System.out.println("(號碼:" + phoneNumber + ")輸入號碼");
System.out.println("(號碼:" + phoneNumber + ")等待接通");
System.out.println("(號碼:" + phoneNumber + ")確認電話已經打通");
}
private void tellDetails(String phoneNumber) {
System.out.println("(號碼:" + phoneNumber + ")詳述情況");
System.out.println("(號碼:" + phoneNumber + ")告知地點");
System.out.println("(號碼:" + phoneNumber + ")記錄指示");
}
private void finishTelephoneTalk(String phoneNumber) {
System.out.println("(號碼:" + phoneNumber + ")掛上電話");
System.out.println("(號碼:" + phoneNumber + ")思考指示");
System.out.println("(號碼:" + phoneNumber + ")行動");
}
public synchronized void execute(String phoneNumber) {
useTelephone(phoneNumber);
tellDetails(phoneNumber);
finishTelephoneTalk(phoneNumber);
}
public static EmergencyTelephone getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new EmergencyTelephone();
}
}
}
return instance;
}
}
進行測試
public class EmergencyTelephoneSample extends Thread {
String phoneNumber;
public EmergencyTelephoneSample(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void run() {
EmergencyTelephone emergencyTelephone = EmergencyTelephone.getInstance();
if (emergencyTelephone != null) {
System.out.println("這通電話號碼是:" + phoneNumber + ",這台電話的生產序號是:" + emergencyTelephone.hashCode());
emergencyTelephone.execute(phoneNumber);
}
}
public static void main(String[] args) {
EmergencyTelephone emergencyTelephone1 = EmergencyTelephone.getInstance();
EmergencyTelephone emergencyTelephone2 = EmergencyTelephone.getInstance();
if (emergencyTelephone1.hashCode() == emergencyTelephone2.hashCode()) {
System.out.println("兩個是同一個物件");
}
Thread t1 = new EmergencyTelephoneSample("119");
Thread t2 = new EmergencyTelephoneSample("110");
t1.start();
t2.start();
}
}
Singleton: EmergencyTelephone
const EmergencyTelephone = (() => {
let instance = null;
function initialize() {
let hashCode = Math.floor(Math.random() * 10000000);
function useTelephone(phoneNumber) {
console.log("(號碼:" + phoneNumber + ")拿起話筒");
console.log("(號碼:" + phoneNumber + ")輸入號碼");
console.log("(號碼:" + phoneNumber + ")等待接通");
console.log("(號碼:" + phoneNumber + ")確認電話已經打通");
}
function tellDetails(phoneNumber) {
console.log("(號碼:" + phoneNumber + ")詳述情況");
console.log("(號碼:" + phoneNumber + ")告知地點");
console.log("(號碼:" + phoneNumber + ")記錄指示");
}
function finishTelephoneTalk(phoneNumber) {
console.log("(號碼:" + phoneNumber + ")掛上電話");
console.log("(號碼:" + phoneNumber + ")思考指示");
console.log("(號碼:" + phoneNumber + ")行動");
}
return {
execute: function (phoneNumber) {
useTelephone(phoneNumber);
tellDetails(phoneNumber);
finishTelephoneTalk(phoneNumber);
},
getHashCode: function () {
return hashCode;
}
};
}
return {
getInstance: function () {
if (instance === null) {
instance = initialize();
}
return instance;
}
}
})();
進行測試
const emergencyTelephoneSample = () => {
const emergencyTelephone1 = EmergencyTelephone.getInstance();
const emergencyTelephone2 = EmergencyTelephone.getInstance();
if (emergencyTelephone1.getHashCode() === emergencyTelephone2.getHashCode()) {
console.log("兩個是同一個物件");
} else {
console.log("出問題,兩個不是同一個物件");
}
console.log("進行第一通電話撥打");
const phoneNumber1 = "119";
console.log("這通電話號碼是:" + phoneNumber1 + ",這台電話的生產序號是:" + emergencyTelephone1.getHashCode());
emergencyTelephone1.execute(phoneNumber1);
console.log("進行第二通電話撥打");
const phoneNumber2 = "110";
console.log("這通電話號碼是:" + phoneNumber2 + ",這台電話的生產序號是:" + emergencyTelephone2.getHashCode());
emergencyTelephone2.execute(phoneNumber2);
}
emergencyTelephoneSample();
Singleton 是非常容易理解的模式,也是十分常見的模式。
要注意的反倒是語言特性,不同語言執行上有細節,例如執行緒的多寡,將影響 Singleton 的效果,這點唯有加強自身對該語言的熟練才能避免。
這是最後一篇 Creational patterns,明天將進入下個類別:Structural patterns 的第一個模式:Adapter 模式。